﻿
using System.Windows.Forms;
namespace System.Drawing
{
	internal static partial class GraphicsExtension
	{
		/// <summary>
		/// Draws image with specified paramters. 
		/// </summary>
		/// <param name="graphics">Graphics on which to draw image</param>
		/// <param name="image">Image to be drawn</param>
		/// <param name="destination">Bounding rectangle for the image </param>
		/// <param name="source">Source rectangle of the image</param>
		/// <param name="alignment">Alignment specifying how image will be aligned against the bounding rectangle </param>
		/// <param name="transparency">Transparency for the image </param>
		/// <param name="grayscale">Value indicating if the image should be gray scaled</param>
		public static void DrawImage(this Graphics graphics, Image image, Rectangle destination, Rectangle source, ContentAlignment alignment = ContentAlignment.TopLeft, float transparency = 1.0f, bool grayscale = false)
		{
			if (graphics == null)
				throw new ArgumentNullException("graphics");
			if (image == null)
				throw new ArgumentNullException("image");
			if (destination.IsEmpty)
				throw new ArgumentNullException("destination");
			if (source.IsEmpty)
				throw new ArgumentNullException("source");
			if (transparency < 0 || transparency > 1.0f)
				throw new ArgumentNullException("transparency");

			Rectangle imageRectangle = GetRectangleFromAlignment(alignment, destination, source.Size);

			if (image != null && !imageRectangle.IsEmpty)
			{
				System.Drawing.Imaging.ColorMatrix colorMatrix = new System.Drawing.Imaging.ColorMatrix();
				if (grayscale)
				{
					colorMatrix.Matrix00 = 1 / 3f;
					colorMatrix.Matrix01 = 1 / 3f;
					colorMatrix.Matrix02 = 1 / 3f;
					colorMatrix.Matrix10 = 1 / 3f;
					colorMatrix.Matrix11 = 1 / 3f;
					colorMatrix.Matrix12 = 1 / 3f;
					colorMatrix.Matrix20 = 1 / 3f;
					colorMatrix.Matrix21 = 1 / 3f;
					colorMatrix.Matrix22 = 1 / 3f;
				}
				colorMatrix.Matrix33 = transparency; //Alpha factor 

				System.Drawing.Imaging.ImageAttributes imageAttributes = new System.Drawing.Imaging.ImageAttributes();
				imageAttributes.SetColorMatrix(colorMatrix);
				graphics.DrawImage(image, imageRectangle, source.X, source.Y, source.Width, source.Height, GraphicsUnit.Pixel, imageAttributes);
			}
		}

		/// <summary>
		/// A method used to draw standard Image and Text content with standard layout options.
		/// </summary>
		/// <param name="graphics">The Graphics object on which to draw.</param>
		/// <param name="bounds">The bounding Rectangle within which to draw.</param>
		/// <param name="text">The text to draw.</param>
		/// <param name="font">The font used to draw text.</param>
		/// <param name="image">The image to draw (this may be null).</param>
		/// <param name="textAlignment">The vertical and horizontal alignment of the text.</param>
		/// <param name="imageAlignment">The vertical and horizontal alignment of the image.</param>
		/// <param name="textImageRelation">The placement of the image and text relative to each other.</param>
		/// <param name="textColor">The color used to draw text.</param>
		/// <param name="wordWrap">set true if text should wrap.</param>
		/// <param name="glowSize">The size in pixels of the glow around text.</param>
		/// <param name="enabled">Draws the content grayed if enabled = false.</param>
		public static void DrawImageAndText(this Graphics graphics, Rectangle bounds, String text, Font font, Image image, System.Drawing.ContentAlignment textAlignment, System.Drawing.ContentAlignment imageAlignment, TextImageRelation textImageRelation, Color textColor, bool wordWrap, int glowSize, bool enabled = true, TextFormatFlags format = TextFormatFlags.TextBoxControl)
		{
			bool horizontalRelation = (int)textImageRelation > 2;
			bool imageHasPreference = textImageRelation == TextImageRelation.ImageBeforeText || textImageRelation == TextImageRelation.ImageAboveText;

			int preferredAlignmentValue = imageHasPreference ? (int)imageAlignment : (int)textAlignment;

			Rectangle contentRectangle = bounds;

			format |= TextFormatFlags.TextBoxControl | (wordWrap ? TextFormatFlags.WordBreak : TextFormatFlags.SingleLine);

			// Get ImageSize
			Size imageSize = image == null ? Size.Empty : image.Size;

			// Get AvailableTextSize
			Size availableTextSize = horizontalRelation ?
				new Size(bounds.Width - imageSize.Width, bounds.Height) :
				new Size(bounds.Width, bounds.Height - imageSize.Height);

			// Get ActualTextSize
			Size actualTextSize = TextRenderer.MeasureText(text, font, availableTextSize, format);

			// Get ContentRectangle based upon TextImageRelation
			if (textImageRelation != 0)
			{
				// Get ContentSize
				Size contentSize = horizontalRelation ?
					new Size(imageSize.Width + actualTextSize.Width, Math.Max(imageSize.Height, availableTextSize.Height)) :
					new Size(Math.Max(imageSize.Width, availableTextSize.Width), imageSize.Height + actualTextSize.Height);

				// Get ContentLocation
				Point contentLocation = bounds.Location;
				if (horizontalRelation)
				{
					if (preferredAlignmentValue % 15 == 1)
						contentLocation.X = bounds.Left;
					else if (preferredAlignmentValue % 15 == 2)
						contentLocation.X = bounds.Left + (bounds.Width / 2 - contentSize.Width / 2);
					else if (preferredAlignmentValue % 15 == 4)
						contentLocation.X = bounds.Right - contentSize.Width;
				}
				else
				{
					if (preferredAlignmentValue <= 4)
						contentLocation.Y = bounds.Top;
					else if (preferredAlignmentValue >= 256)
						contentLocation.Y = bounds.Bottom - contentSize.Height;
					else
						contentLocation.Y = bounds.Top + (bounds.Height / 2 - contentSize.Height / 2);
				}

				contentRectangle = new Rectangle(contentLocation, contentSize);
			}

			if (image != null)
			{
				// Get ActualImageBounds
				Rectangle actualImageBounds = new Rectangle(bounds.Location, imageSize);
				if (horizontalRelation)
				{
					actualImageBounds.X = imageHasPreference ? contentRectangle.X : contentRectangle.Right - imageSize.Width;
					actualImageBounds.Y = (int)imageAlignment <= 4 ?
						contentRectangle.Y : (int)imageAlignment >= 256 ?
						contentRectangle.Bottom - imageSize.Height :
						contentRectangle.Y + contentRectangle.Height / 2 - imageSize.Height / 2;
				}
				else if (textImageRelation == 0)
				{
					if ((int)imageAlignment <= 4)
						actualImageBounds.Y = bounds.Top;
					else if ((int)imageAlignment >= 256)
						actualImageBounds.Y = bounds.Bottom - imageSize.Height;
					else
						actualImageBounds.Y = bounds.Top + (bounds.Height / 2 - imageSize.Height / 2);

					if ((int)imageAlignment % 15 == 1)
						actualImageBounds.X = bounds.Left;
					else if ((int)imageAlignment % 15 == 2)
						actualImageBounds.X = bounds.Left + (bounds.Width / 2 - imageSize.Width / 2);
					else if ((int)imageAlignment % 15 == 4)
						actualImageBounds.X = bounds.Right - imageSize.Width;
				}
				else
				{
					actualImageBounds.Y = imageHasPreference ? contentRectangle.Y : contentRectangle.Bottom - imageSize.Height;
					actualImageBounds.X = (int)imageAlignment % 15 == 1 ?
						contentRectangle.X : (int)imageAlignment % 15 == 2 ?
						contentRectangle.X + contentRectangle.Width / 2 - imageSize.Width / 2 :
						contentRectangle.Right - imageSize.Width;
				}

				// Draw Image
				if (enabled)
					graphics.DrawImage(image, actualImageBounds);
				else
					ControlPaint.DrawImageDisabled(graphics, image, actualImageBounds.X, actualImageBounds.Y, Color.Transparent);
			}

			// Get ActualTextBounds
			Rectangle actualTextBounds = new Rectangle(Point.Empty, actualTextSize);
			if (horizontalRelation)
			{
				actualTextBounds.X = imageHasPreference ? contentRectangle.Right - actualTextSize.Width : contentRectangle.X;
				actualTextBounds.Y = (int)textAlignment <= 4 ?
					contentRectangle.Y : (int)textAlignment >= 256 ?
					contentRectangle.Bottom - actualTextSize.Height :
					contentRectangle.Y + (contentRectangle.Height / 2) - (actualTextSize.Height / 2);
			}
			else if (textImageRelation == 0)
			{
				if ((int)textAlignment <= 4)
					actualTextBounds.Y = bounds.Top;
				else if ((int)textAlignment >= 256)
					actualTextBounds.Y = bounds.Bottom - actualTextSize.Height;
				else
					actualTextBounds.Y = bounds.Top + (bounds.Height / 2 - actualTextSize.Height / 2);

				if ((int)textAlignment % 15 == 1)
					actualTextBounds.X = bounds.Left;
				else if ((int)textAlignment % 15 == 2)
					actualTextBounds.X = bounds.Left + (bounds.Width / 2 - actualTextSize.Width / 2);
				else if ((int)textAlignment % 15 == 4)
					actualTextBounds.X = bounds.Right - actualTextSize.Width;
			}
			else
			{
				actualTextBounds.Y = imageHasPreference ? contentRectangle.Bottom - actualTextSize.Height : contentRectangle.Y;
				actualTextBounds.X = (int)textAlignment % 15 == 1 ?
					contentRectangle.X : (int)textAlignment % 15 == 2 ?
					contentRectangle.X + contentRectangle.Width / 2 - actualTextSize.Width / 2 :
					contentRectangle.Right - actualTextSize.Width;
			}

			// Draw text
			TextRenderer.DrawText(graphics, text, font, actualTextBounds, textColor, format);
		}

		internal static Rectangle GetRectangleFromAlignment(ContentAlignment alignment, Rectangle destination, Size size)
		{
			if ((alignment & (ContentAlignment.BottomLeft | ContentAlignment.MiddleLeft | ContentAlignment.TopLeft)) != 0)
				destination.Width = Math.Min(size.Width, destination.Width);
			else if ((alignment & (ContentAlignment.BottomRight | ContentAlignment.MiddleRight | ContentAlignment.TopRight)) != 0)
				destination.X = destination.Width - Math.Min(size.Width, destination.Width);
			else
				destination.X = (destination.Width - Math.Min(size.Width, destination.Width)) / 2;

			if ((alignment & (ContentAlignment.TopCenter | ContentAlignment.TopLeft | ContentAlignment.TopRight)) != 0)
				destination.Height = Math.Min(size.Height, destination.Height);
			else if ((alignment & (ContentAlignment.BottomCenter | ContentAlignment.BottomLeft | ContentAlignment.BottomRight)) != 0)
				destination.Y = destination.Height - Math.Min(size.Height, destination.Height);
			else
				destination.Y = (destination.Height - Math.Min(size.Height, destination.Height)) / 2;

			return destination;
		}
	}
}